/** @file caStore.h
 * Defines and prototypes that deal with Stores.
 */

/*
 * Licensed to the ConArtist Development Team (CADT).
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 * The CADT licenses this file to you under the Apache License, 
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef CASTORE_H
#define CASTORE_H

#include <apr_file_info.h>
#include <apr_tables.h>
#include <apr_hash.h>
#include <apr_thread_rwlock.h>

#include <libxml/tree.h>

#include "conartist.h"

/** @defgroup STORES ConArtist Stores
 * @{
 */

/* Forward declarations */
struct caStore;

/** @fn struct caStore *caStoreOpen(const char *type, const char *name,
                                    xmlNode *node)
 * Open the store. This attempts to make the store fully available
 * for use. When the function returns the store is usable.
 * @param type Type of store to be created
 * @param name Name to use for this store
 * @param node Pointer to the XML nodes that contain configuration
 *             information for this store
 * @return Pointer to store if succesful or NULL in case of failure.
 */
apr_status_t caStoreOpen(const char *, const char *, xmlNode *);

/** @fn apr_status_t caStoreClose(struct caStore *store);
 * Close the store. This marks the store as complete and will see all
 * changes flushed and actions terminated.
 * @param store Pointer to the store to close.
 */
apr_status_t caStoreClose(struct caStore *);

/** @fn apr_status_t caStoreFind(struct caStore **ss, const char *name)
 * Search for a store using the type and path it was assigned.
 * @param ss Pointer to returned value
 * @param name Name of store to find
 */
apr_status_t caStoreFind(struct caStore **, const char *);

/** @defgroup DIR Directory functions 
 * @ingroup STORES
 * In order to permit as many different types of store mechanisms to
 * be available, the standard POSIX directory functions are abstracted
 * out and implemented by the modules that provide the functionality.
 * @note These functions are not meant to be used directly, but are 
 *       intended for internal use.
 * @{
 */

/** The caDir structure represents a directory within a store that has
 * been opened.
 */
struct caDir {
    apr_pool_t *pool;            /**< Pool to use for memory allocations */
    struct caStore *store;       /**< Pointer to store module we interact with */
    const char *path;            /**< Relative path we were opened for */
    void *privateData;           /**< Pointer to private data */
};

/** This structure represents an entry within an opened caDir structure.
 */
struct caDirEntry {
    apr_finfo_t finfo;           /**< Structure containing returned data */
};

/** @fn struct caDir *caOpenDirectory(struct caStore *s, const char *path,
                                      apr_pool_t *pool);
 * Open the directory and make it's contents available via the returned
 * caDir structure pointer.
 * @note No ordering of the contents of the directory is assured.
 * @param s The store object to work within
 * @param path The relative path within the store to make available
 * @param pool The pool to use for memory allocations
 */
struct caDir *caOpenDirectory(struct caStore *, const char *, apr_pool_t *);

/** @fn apr_status_t caReadDirectory(struct caDirEntry *ent, 
                                     struct caDir *dir)
 * Read the next entry within the directory available in the caDir pointer
 * supplied and fill in the fields within the supplied caDirEntry structure
 * as required.
 * @param dir The caDir pointer to use.
 * @param ent The caDirEntry structure to fill in
 */
apr_status_t caReadDirectory(struct caDirEntry *, struct caDir *);

/** @fn apr_status_t caCloseDirectory(struct caDir *dir)
 * Finish using the directory reflected by the supplied caDir structure.
 * @param dir Pointer to the caDir structure to finish using.
 */
apr_status_t caCloseDirectory(struct caDir *);

/** @} */

/** @defgroup FILEIO File I/O 
 * @ingroup STORES
 * Functions to read/write from files within stores.
 * @note These functions are not meant to be used directly, but are
 *       intended for internal use.
 * @{
 */

/** The caFile struct is used to represent a "file". The opened I/O
 * channel is assumed to be read/write and is meant to be abstract enough
 * that mechanisms can implement any type of operation they need to.
 */
struct caFile {
    apr_pool_t *pool;            /**< Pool we were allocated from */
    char *path;                  /**< Path we represent */
    struct caStoreModule *store; /**< Pointer to store module we interact with */
    void *privateData;           /**< Pointer to private data */
};

/** @fn apr_status_t caOpen(struct caFile **fh, struct caStore *store,
                            const char *path, apr_pool_t *pool)
 * Open the file 'path' from the given store 'store'.
 * @param fh Pointer to a pointer to the created file handle
 * @param store Pointer to caStore structure we're using
 * @param path Path of file to open (relative to store root)
 * @param pool The pool to use for memory allocations
 * @note File is opened as Read + Write
 */
apr_status_t caOpen(struct caFile **, struct caStore *, const char *, 
                    apr_pool_t *);

/** @fn apr_status_t caClose(struct caFile *fh)
 * Close an open file handle.
 * @param fh Pointer to filehandle to close
 */
apr_status_t caClose(struct caFile *);

/** @fn apr_status_t caRead(struct caFile *fh, char *data, 
                            apr_size_t *len)
 * Read data from an open file handle.
 * @param fh File to read from
 * @param data Pointer to buffer to be read into
 * @param len  Length of data to be written
 * @returns APR_SUCCESS if any data is read. The number of bytes 
 *          read will be stored in the len paramter.
 */
apr_status_t caRead(struct caFile *, char *, apr_size_t *);

/** @fn apr_status_t caWrite(struct caFile *fh, const char *data, 
                             apr_size_t *len)
 * Write data to an open file handle.
 * @param fh File to write to
 * @param data Data to be written
 * @param len  Length of data to be written
 * @returns APR_SUCCESS if any data is written. The number of bytes 
 *          written will be stored in the len paramter.
 */
apr_status_t caWrite(struct caFile *, const char *, apr_size_t *);

/** @fn apr_status_t caFileGet(char **buffer, apr_size_t *bytes, 
                               const char *path, struct caStore *store,
                               apr_pool_t *p)
 * Read the entire file into a memory buffer and return pointers to
 * the buffer and it's size.
 * @param buffer Pointer to the created buffer
 * @param bytes Size of the created buffer
 * @param path Path of file to read within the store
 * @param store Pointer to the store to use
 * @param p Pool to use for the allocations
 */
apr_status_t caFileGet(char **, apr_size_t *, const char *, struct caStore *,
                       apr_pool_t *);

/** @} */

/** Each module that implements a type of Store should define one of
 * these structures.
 */
struct caStoreModule {
    struct caModule module;   /**< Basic module information */
    char *type;               /**< String identifying type of module */
    /** Open the store */
    apr_status_t (*openStore)(struct caStore *);

    /** Close the store */
    void (*closeStore)(struct caStore *);

    /** Return the local system filepath to the file/directory given
     * using the supplied pool for memory allocations.
     */
    apr_status_t (*getPath)(char **, const char *, apr_pool_t *, void *);

    /** Open the directory using the pool supplied for memory
     * allocations.
     * @note If this is not supplied then apr_dir_open() will be used.
     */
    apr_status_t (*openDir)(struct caDir **, const char *, apr_pool_t *, 
                            void *);
    /** Read the next entry from a directory.
     * @note If this is not supplied apr_dir_read() will be used.
     */
    apr_status_t (*readDir)(struct caDirEntry *, struct caDir *, void *);
    /** Close the opened directory.
     * @note If not set apr_dir_close() will be called.
     */
    apr_status_t (*closeDir)(struct caDir *, void *);

    /** Open a file 
     * @note If not set apr_file_open() will be called.
     */
    apr_status_t (*openFile)(struct caFile **, const char *, apr_pool_t *,
                             void *);
    /** Close a file */
    apr_status_t (*closeFile)(struct caFile *);
    /** Read data from a file
     * @note If not supplied apr_file_read will be used.
     */
    apr_status_t (*readFile)(struct caFile *, char *, apr_size_t *);
    /** Write data to a file
     * @note If not supplied apr_file_write will be used.
     */
    apr_status_t (*writeFile)(struct caFile *, const char *, apr_size_t *);

};

/** The caStore structure contains all the information for a defined
 * store.
 */
struct caStore {
    apr_pool_t   *st_pool;       /**< memory pool for allocations */

    char *name;                  /**< Name assigned to store */
    char *baseDirectory;         /**< Base directory for local files */
    void *privateData;           /**< Pointer to mechanism specific data
                                  *   structure */
    apr_table_t *config;         /**< Configuration information */
    apr_thread_rwlock_t *lock;   /**< Store RW lock */
    apr_hash_t *notifyHash;      /**< Hash of notification structures */

    /** Pointer to module that implements the store type */
    struct caStoreModule *module;
};

/** @defgroup NOTIFY Notifications
 * Stores provide notifications of store changes via a series of
 * callbacks that can be registered with the store.
 * @note These functions are not meant to be called externally.
 * @ingroup STORES
 * @{
 */

#define CAN_ADD         0x0001    /**< Entity added to a store */
#define CAN_MODIFY      0x0002    /**< Entity has been changed */
#define CAN_DELTE       0x0004    /**< Entity has been deleted */
#define CAN_ALL         0x0007    /**< All changes */

/** @typedef void storeNotifyCb(void *data, const char *path, int which)
 * Callback function that is registered with a store. The callback
 * will be triggered when one of the changes registered is made.
 * The file that triggers the change will be passed via the 'path'
 * parameter.
 * @param data Pointer to data registered with callback
 * @param path Path of change that triggered the change
 * @param which CAN_ constant for the change that occurred
 */
typedef void storeNotifyCb(void *, const char *, int);

/** @fn apr_status_t storeNotifyAdd(struct caStore *store, 
                                    const char *path, int which, 
                                    storeNotifyCb *callback, void *data)
 * Add a callback to a store for the paths identified by 'path' and
 * events identified by 'which'.
 * @param store The store to add the notification for
 * @param path The path(s) to notify for. This may be the '*' wildcard.
 * @param which OR'd set of CAN_ constants to identify the events to
 *              call the notification callback for
 * @param callback pointer to the function to call
 * @param data Pointer to the data to pass to the function when called
 */
apr_status_t storeNotifyAdd(struct caStore *, const char *, int, 
                            storeNotifyCb *, void *);

/** @fn apr_status_t storeNotifyChange(struct caStore *store, 
                                       const char *path, int which, 
                                       storeNotifyCb *callback, void *data)
 * Change the events that a callback is triggered for.
 * @note The callback must already be registered.
 * @note This function cannot be used to change the function or data
 *       pointers. If this is desired a new callback should be added and
 *       the previous one deleted.
 * @param store The store to add the notification for
 * @param path The path(s) to notify for. This may be the '*' wildcard.
 * @param which New value of OR'd CAN_ constants to call the notification
 *              callback for
 * @param callback pointer to the function to call
 * @param data Pointer to the data to pass to the function when called
 */

apr_status_t storeNotifyChange(struct caStore *, const char *, int, 
                               storeNotifyCb *, void *);

/** @} */

/** @} */

#endif /* CASTORE_H */
